//*********************************************************
//
//    Copyright (c) Microsoft. All rights reserved.
//    This code is licensed under the Apache License, Version 2.0.
//    THIS CODE IS PROVIDED *AS IS* WITHOUT WARRANTY OF
//    ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING ANY
//    IMPLIED WARRANTIES OF FITNESS FOR A PARTICULAR
//    PURPOSE, MERCHANTABILITY, OR NON-INFRINGEMENT.
//
//*********************************************************

namespace Microsoft.Research.eResearch.RuntimeServices.WorkflowOutput
{
    using System;
    using System.Workflow.ComponentModel;
    using System.Workflow.Runtime;
    using System.Workflow.Runtime.Tracking;
    using Microsoft.Research.eResearch.RuntimeServices.Common;

    /// <summary>
    /// This class contains the Output Tracking Service Functionality.
    /// </summary>
    public class OutputTrackingService : TrackingService
    {

        /// <summary>
        /// Must be overridden in the derived class, and when implemented, returns the tracking profile for the specified workflow instance.
        /// </summary>
        /// <param name="workflowInstanceId">The <see cref="T:System.Guid"/> of the workflow instance.</param>
        /// <returns>
        /// A <see cref="T:System.Workflow.Runtime.Tracking.TrackingProfile"/>.
        /// </returns>
        protected override TrackingProfile GetProfile(Guid workflowInstanceId)
        {
            return GetDefaultProfile(workflowInstanceId);
        }

        /// <summary>
        /// Must be overridden in the derived class, and when implemented, returns the tracking profile, qualified by version, for the specified workflow <see cref="T:System.Type"/>.
        /// </summary>
        /// <param name="workflowType">The <see cref="T:System.Type"/> of the workflow.</param>
        /// <param name="profileVersionId">The <see cref="T:System.Version"/> of the tracking profile.</param>
        /// <returns>
        /// A <see cref="T:System.Workflow.Runtime.Tracking.TrackingProfile"/>.
        /// </returns>
        protected override TrackingProfile GetProfile(Type workflowType, Version profileVersionId)
        {
            return GetDefaultProfile(WorkflowEnvironment.WorkflowInstanceId);
        }

        /// <summary>
        /// Must be overridden in the derived class, and when implemented, returns the channel that the run-time tracking infrastructure uses to send tracking records to the tracking service.
        /// </summary>
        /// <param name="parameters">The <see cref="T:System.Workflow.Runtime.Tracking.TrackingParameters"/> associated with the workflow instance.</param>
        /// <returns>
        /// The <see cref="T:System.Workflow.Runtime.Tracking.TrackingChannel"/> that is used to send tracking records to the tracking service.
        /// </returns>
        protected override TrackingChannel GetTrackingChannel(TrackingParameters parameters)
        {
            return new OutputTrackingChannel(this.Runtime, parameters);
        }

        /// <summary>
        /// Must be overridden in the derived class, and when implemented, retrieves the tracking profile for the specified workflow type if one is available.
        /// </summary>
        /// <param name="workflowType">The <see cref="T:System.Type"/> of the workflow for which to get the tracking profile.</param>
        /// <param name="profile">When this method returns, contains the <see cref="T:System.Workflow.Runtime.Tracking.TrackingProfile"/> to load. This parameter is passed un-initialized.</param>
        /// <returns>
        /// true if a <see cref="T:System.Workflow.Runtime.Tracking.TrackingProfile"/> for the specified workflow <see cref="T:System.Type"/> is available; otherwise, false. If true, the <see cref="T:System.Workflow.Runtime.Tracking.TrackingProfile"/> is returned in <paramref name="profile"/>.
        /// </returns>
        protected override bool TryGetProfile(Type workflowType, out TrackingProfile profile)
        {
            profile = GetDefaultProfile(WorkflowEnvironment.WorkflowInstanceId);
            return true;
        }

        /// <summary>
        /// Must be overridden in the derived class, and when implemented, retrieves a new tracking profile for the specified workflow instance if the tracking profile has changed since it was last loaded.
        /// </summary>
        /// <param name="workflowType">The <see cref="T:System.Type"/> of the workflow instance.</param>
        /// <param name="workflowInstanceId">The <see cref="T:System.Guid"/> of the workflow instance.</param>
        /// <param name="profile">When this method returns, contains the <see cref="T:System.Workflow.Runtime.Tracking.TrackingProfile"/> to load. This parameter is passed un-initialized.</param>
        /// <returns>
        /// true if a new <see cref="T:System.Workflow.Runtime.Tracking.TrackingProfile"/> should be loaded; otherwise, false. If true, the <see cref="T:System.Workflow.Runtime.Tracking.TrackingProfile"/> is returned in <paramref name="profile"/>.
        /// </returns>
        protected override bool TryReloadProfile(Type workflowType, Guid workflowInstanceId, out TrackingProfile profile)
        {
            profile = GetDefaultProfile(workflowInstanceId);
            return true;
        }

        /// <summary>
        /// Gets the default profile.
        /// </summary>
        /// <param name="workflowInstanceId">The workflow instance id.</param>
        /// <returns></returns>
        private TrackingProfile GetDefaultProfile(Guid workflowInstanceId)
        {
            TrackingProfile profile = new TrackingProfile();
            profile.Version = new Version(1, 0, 0, 0);

            // HACK : Use reflection to grab a reference to the PRIVATE root activity inside the WorkflowRuntime
            Activity rootActivity = WorkflowRuntimeHelper.GrabRootActivity(this.Runtime, workflowInstanceId);


            // Add a user trackpoint to track the user events.
            UserTrackPoint userTrackPoint = new UserTrackPoint();
            UserTrackingLocation userLocation = new UserTrackingLocation();
            userLocation.ActivityType = typeof(Activity);
            userLocation.MatchDerivedActivityTypes = true;
            userLocation.ArgumentType = typeof(object);
            userLocation.MatchDerivedArgumentTypes = true;
            userTrackPoint.MatchingLocations.Add(userLocation);

            profile.UserTrackPoints.Add(userTrackPoint);

            // Use reflection to create tracking data extracts for every property of every activity
            WorkflowRuntimeHelper.PopulateOutputTrackingExtracts(rootActivity, profile);

            return profile;
        }
    }
}
